package com.jfirer.mvc.boot;

import com.jfirer.baseutil.reflect.ReflectUtil;
import com.jfirer.mvc.core.EasyMvcDispathServlet;
import io.undertow.Handlers;
import io.undertow.Undertow;
import io.undertow.Undertow.Builder;
import io.undertow.server.handlers.PathHandler;
import io.undertow.server.handlers.resource.ClassPathResourceManager;
import io.undertow.servlet.Servlets;
import io.undertow.servlet.api.DeploymentInfo;
import io.undertow.servlet.api.DeploymentManager;
import io.undertow.servlet.api.FilterInfo;
import io.undertow.servlet.api.ServletInfo;

import javax.net.ssl.SSLContext;
import javax.servlet.DispatcherType;
import javax.servlet.Filter;
import javax.servlet.MultipartConfigElement;
import javax.servlet.annotation.MultipartConfig;
import javax.servlet.annotation.WebFilter;

public class BootApplication
{

    private Filter[]   filters;
    private SSLContext sslContext;
    private String     appName;
    private String     resourcesPrefix;
    private int        port = -1;
    private Class<?>   configClass;
    private int        ioThreads;

    public BootApplication(Class<?> configClass, Filter... filters)
    {
        this.configClass = configClass;
        AppInfo appInfo = configClass.getAnnotation(AppInfo.class);
        if (appInfo == null)
        {
            throw new NullPointerException();
        }
        this.filters = filters;
        appName = appInfo.appName();
        resourcesPrefix = appInfo.prefix();
        port = appInfo.port();
    }

    public BootApplication(Class<?> configClass, String appName, String resourcesPrefix, int port, Filter... filters)
    {
        this.configClass = configClass;
        this.appName = appName;
        this.port = port;
        this.resourcesPrefix = resourcesPrefix;
        this.filters = filters;
        this.configClass = configClass;
    }

    public BootApplication(Class<?> configClass, SSLContext sslContext, Filter... filters)
    {
        this(configClass, filters);
        this.sslContext = sslContext;
    }

    public void start()
    {
        ioThreads = System.getProperty("mvc.ioThreads") == null ? Runtime.getRuntime().availableProcessors() * 2 : Integer.parseInt(System.getProperty("mvc.ioThreads"));
        int workerThreads = System.getProperty("mvc.workerThreads") == null ? ioThreads * 8 : Integer.parseInt(System.getProperty("mvc.workerThreads"));
        try
        {
            ServletInfo servletInfo = Servlets.servlet("EasyMvcDispathServlet", EasyMvcDispathServlet.class)//
                    .addMapping("/*")//
                    .setEnabled(true)//
                    .setLoadOnStartup(1)//
                    .setAsyncSupported(true)//
                    .setMultipartConfig(new MultipartConfigElement(EasyMvcDispathServlet.class.getAnnotation(MultipartConfig.class)))//
                    .addInitParam(EasyMvcDispathServlet.CONFIG_CLASS_NAME, configClass.getName());
            DeploymentInfo servletBuilder = Servlets.deployment()//
                    .setClassLoader(BootApplication.class.getClassLoader())//
                    .setContextPath(appName)//
                    .setDeploymentName("bootstarter")//
                    .addServlets(servletInfo);
            servletBuilder.setResourceManager(new ClassPathResourceManager(Thread.currentThread().getContextClassLoader(), resourcesPrefix));
            for (Filter filter : filters)
            {
                Class<? extends Filter> ckass = filter.getClass();
                if (ckass.isAnnotationPresent(WebFilter.class) == false)
                {
                    continue;
                }
                WebFilter  webFilter  = ckass.getAnnotation(WebFilter.class);
                FilterInfo filterInfo = new FilterInfo(webFilter.filterName(), ckass);
                filterInfo.setAsyncSupported(webFilter.asyncSupported());
                servletBuilder.addFilter(filterInfo);
                for (String url : webFilter.value())
                {
                    servletBuilder.addFilterUrlMapping(webFilter.filterName(), url, DispatcherType.FORWARD);
                    servletBuilder.addFilterUrlMapping(webFilter.filterName(), url, DispatcherType.INCLUDE);
                    servletBuilder.addFilterUrlMapping(webFilter.filterName(), url, DispatcherType.REQUEST);
                    servletBuilder.addFilterUrlMapping(webFilter.filterName(), url, DispatcherType.ASYNC);
                }
            }
            DeploymentManager manager = Servlets.defaultContainer().addDeployment(servletBuilder);
            manager.deploy();
            PathHandler path = Handlers.path(Handlers.redirect(appName)).addPrefixPath(appName, manager.start());
            Builder builder = Undertow.builder()//
                    .setIoThreads(ioThreads)//
                    .setWorkerThreads(workerThreads)//
                    .addHttpListener(port, "0.0.0.0")//
                    .setHandler(path);
            if (sslContext != null)
            {
                builder.addHttpsListener(8443, "0.0.0.0", sslContext);
            }
            Undertow server = builder.build();
            server.start();
        }
        catch (Exception e)
        {
            ReflectUtil.throwException(e);
        }
    }
}
